/**
 * @file	Layout.cpp
 * @brief	This is a class which uses to manager elements in a layout.
 * @author	Shaokp
 * @date	2011-11-24
 * @version	0.1
 */

#include "Layout.h"

PathGridNode::PathGridNode(Point pos)
{
	mCoordinate = pos;
	mIsOccupied = false;
	mIsTarget = false;
	mIsSource = false;
	mWeight = std::numeric_limits<unsigned int>::max();
}

void PathGridNode::setCoordinate(Point pos)
{
	mCoordinate = pos;
}

Point PathGridNode::getCoordinate() const
{
	return mCoordinate;
}

void PathGridNode::setOccupiedFlag(bool flag)
{
	mIsOccupied = flag;
}

bool PathGridNode::isOccupied() const
{
	return mIsOccupied;
}

void PathGridNode::setTargetFlag(bool flag)
{
	mIsTarget = flag;
}

bool PathGridNode::isTarget() const
{
	return mIsTarget;
}

void PathGridNode::setSourceFlag(bool flag)
{
	mIsSource = flag;
}

bool PathGridNode::isSource() const
{
	return mIsSource;
}

void PathGridNode::setWeight(unsigned int weight)
{
	mWeight = weight;
}

unsigned int PathGridNode::getWeight() const
{
	return mWeight;
}

Layout::Layout(LayoutSettings settings)
{
	mElements.clear();
	mSettings = settings;
}

Layout::~Layout()
{
	std::map<std::string, Element*>::iterator iter;
	for(iter = mElements.begin(); iter != mElements.end(); ++iter)
	{
		delete iter->second;
		iter->second = 0;
	}

	mElements.clear();
}

bool Layout::addElement(Element *element)
{
	std::map<std::string, Element*>::iterator iter;
	if((iter = mElements.find(element->getName())) == mElements.end())
	{
		mElements.insert(std::pair<std::string, Element *>(element->getName(), element));
		return true;
	}
	return false;
}

bool Layout::delElement(Element *element)
{
	std::map<std::string, Element*>::iterator iter;
	if((iter = mElements.find(element->getName())) != mElements.end())
	{
		mElements.erase(iter);
		return true;
	}
	return false;
}

Element *Layout::getElement(std::string &objName) const
{
	std::map<std::string, Element*>::iterator iter;
	if((iter = mElements.find(objName)) != mElements.end())
	{
		return iter->second;
	}
	return 0;
}

// We use this function to create grid according elements in layout.
void Layout::createGrid(std::vector<std::vector<PathGridNode> > &grid)
{
	grid.clear();
	std::vector<double> x_v, y_v;
	std::map<std::string, Element*>::iterator iter;
	
	double maxx = mSettings.mModuleSize * mSettings.mModuleNumX + mSettings.mModuleSpace * (mSettings.mModuleNumX - 1) + mSettings.mCellBufferExt * 2;
	double maxy = mSettings.mModuleSize * mSettings.mModuleNumY + mSettings.mModuleSpace * (mSettings.mModuleNumY - 1) + mSettings.mCellBufferExt * 2;

	// Create the grid from cell bbox
	std::vector<PathGridNode> tmpv;
	PathGridNode p1(Point(0, 0));
	tmpv.push_back(p1);
	PathGridNode p2(Point(maxx, 0));
	tmpv.push_back(p2);
	grid.push_back(tmpv);
	tmpv.clear();
	PathGridNode p3(Point(0, maxy));
	tmpv.push_back(p3);
	PathGridNode p4(Point(maxx, maxy));
	tmpv.push_back(p4);
	grid.push_back(tmpv);

	for(iter = mElements.begin(); iter != mElements.end(); ++iter)
	{
		addOneElementIntoGrid(grid, iter->second);
	}

	for(int i = 0; i != grid[0].size(); ++i)
	{
		grid[0][i].setOccupiedFlag(true);
		grid[grid.size()-1][i].setOccupiedFlag(true);
	}
	for(int i = 0; i != grid.size(); ++i)
	{
		grid[i][0].setOccupiedFlag(true);
		grid[i][grid[0].size()-1].setOccupiedFlag(true);
	}
}

void Layout::addOneElementIntoGrid(std::vector<std::vector<PathGridNode> > &grid, Element *element)
{
	double module_buffer = mSettings.mConnectionModuleSpace + mSettings.mConnectionWidth / 2;
	double connection_buffer = mSettings.mConnectionSpace + mSettings.mConnectionWidth / 2;
	std::vector<Point> vertexs;

	// Firstly, we add ends of element buffer.
	// Need check if overlap with other elements.
	// Then, we add ends of element.
	if(element->getType() == Element::PATH)
		element->getVertexs(vertexs, connection_buffer);
	else if(element->getType() == Element::MODULE)
		element->getVertexs(vertexs, module_buffer);
	else
		return;

	for(int i = 0; i != vertexs.size(); ++i)
		addOnePointIntoGrid(grid, vertexs[i], false);

	vertexs.clear();
	element->getVertexs(vertexs);
	for(int i = 0; i != vertexs.size(); ++i)
		addOnePointIntoGrid(grid, vertexs[i], true);
}

void Layout::addOnePointIntoGrid(std::vector<std::vector<PathGridNode> > &grid, Point point, bool isOccupied)
{
	bool flagx = false, flagy = false; // If the gridNode of point located is created, created: true, else false.
	int i, j;

	// Firstly, we should check if the point has been located in grid.
	// If not, we should find the correct location of current point.
	for(i = 0; i != grid[0].size(); ++i)
	{
		if(point.x < grid[0][i].getCoordinate().x)
			break;
		else if(point.x == grid[0][i].getCoordinate().x)
		{
			flagx = true;
			break;
		}
	}
	for(j = 0; j != grid.size(); ++j)
	{
		if(point.y < grid[j][0].getCoordinate().y)
			break;
		else if(point.y == grid[j][0].getCoordinate().y)
		{
			flagy = true;
			break;
		}
	}

	// If flagx != true, we need to create a new column.
	// If flagy != true, we need to create a new row.
	if(!flagx)
	{
		for(int m = 0; m != grid.size(); ++m)
		{
			PathGridNode node(Point(point.x, grid[m][0].getCoordinate().y));
			grid[m].insert(grid[m].begin()+i, node);
		}
		
	}
	if(!flagy)
	{
		std::vector<PathGridNode> tmpv;
		for(int m = 0; m != grid[0].size(); ++m)
		{
			PathGridNode node(Point(grid[0][m].getCoordinate().x, point.y));
			tmpv.push_back(node);
		}
		grid.insert(grid.begin()+j, tmpv);
	}
	grid[j][i].setOccupiedFlag(isOccupied);

	// When we add a point of buffer ends, the created column and row can not around by occupied point.
	// (If yew, it means overlap with other element)
	if(!isOccupied)
	{
		for(int m = 0; m != grid.size(); ++m)
		{
			if(grid[m][i-1].isOccupied() || grid[m][i+1].isOccupied())
			{
				grid[m][i].setOccupiedFlag(true);
				grid[m][i-1].setOccupiedFlag(true);
				grid[m][i+1].setOccupiedFlag(true);
			}
		}
		for(int m = 0; m != grid[0].size(); ++m)
		{
			if(grid[j-1][m].isOccupied() || grid[j+1][m].isOccupied())
			{
				grid[j][m].setOccupiedFlag(true);
				grid[j-1][m].setOccupiedFlag(true);
				grid[j+1][m].setOccupiedFlag(true);
			}
		}
	}
}
